<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>图解Git</title>
  <link rel='stylesheet' type='text/css' href='visual-git-guide.css'>
  <script type="text/javascript" src='visual-git-guide.js'></script>
</head>
<body onload="replace_all_PNGs();">
  <h1 id="top">图解Git</h1>

  <div id="language-box">
    <a>其他语言:</a>
    <ul>
      <li><a href='index-de.html'>Deutsch</a></li>
      <li><a href='index-en.html'>English</a></li>
      <li><a href='index-es.html'>Español</a></li>
      <li><a href='index-fr.html'>Français</a></li>
      <li><a href='index-it.html'>Italiano</a></li>
      <li><a href='index-ja.html'>日本語</a></li>
      <li><a href='index-ko.html'>한국어</a></li>
      <li><a href='index-pl.html'>Polski</a></li>
      <li><a href='index-pt.html'>Português</a></li>
      <li><a href='index-ru.html'>Русский</a></li>
      <li><a href='index-sk.html'>Slovenčina</a></li>
      <li><a href='index-vi.html'>Tiếng Việt</a></li>
      <li class="selected">简体中文</li>
      <li><a href='index-zh-tw.html'>正體中文</a></li>
    </ul>
  </div>

  <p id="link-to-png">如果图片不能显示，试试<a href="?no-svg">非SVG版</a></p>

  <p id="link-to-svg">SVG图片已被禁用
  <a href="index.html">（重新启用SVG）</a></p>

  <p>此页图解git中的最常用命令。如果你稍微理解git的工作原理，这篇文章能够让你理解的更透彻。
  如果你想知道这个站点怎样产生，请前往<a href='http://github.com/MarkLodato/visual-git-guide'>GitHub
    repository</a>。</p>



  <h2 id="contents">正文</h2>
  <ol>
    <li><a href="#basic-usage">基本用法</a></li>
    <li><a href="#conventions">约定</a></li>
    <li><a href="#commands-in-detail">命令详解</a>
      <ol>
        <li><a href="#diff">Diff</a></li>
        <li><a href="#commit">Commit</a></li>
        <li><a href="#checkout">Checkout</a></li>
        <li><a href="#detached">Detached HEAD(匿名分支提交)</a></li>
        <li><a href="#reset">Reset</a></li>
        <li><a href="#merge">Merge</a></li>
        <li><a href="#cherry-pick">Cherry Pick</a></li>
        <li><a href="#rebase">Rebase</a></li>
      </ol>
    </li>
    <li><a href="#technical-notes">技术说明</a></li>
  </ol>

  <h2 id="basic-usage">基本用法</h2>

  <div class="center"><img src='basic-usage.svg.png'></div>

  <p>上面的四条命令在工作目录、暂存目录(也叫做索引)和仓库之间复制文件。</p>

  <ul>

    <li><code>git add <em>files</em></code> 把当前文件放入暂存区域。</li>

    <li><code>git commit</code> 给暂存区域生成快照并提交。</li>

    <li><code>git reset -- <em>files</em></code> 用来撤销最后一次<code>git add <em>files</em></code>，你也可以用<code>git reset</code>
    撤销所有暂存区域文件。</li>

    <li><code>git checkout -- <em>files</em></code> 把文件从暂存区域复制到工作目录，用来丢弃本地修改。</li>

  </ul>

  <p>你可以用 <code>git reset -p</code>, <code>git checkout -p</code>, or
  <code>git add -p</code>进入交互模式。</p>

  <p>也可以跳过暂存区域直接从仓库取出文件或者直接提交代码。</p>

  <div class="center"><img src='basic-usage-2.svg.png'></div>

  <ul>

    <li><code>git commit -a </code> 相当于运行 <tt>git add</tt>
    把所有当前目录下的文件加入暂存区域再运行。<tt>git commit</tt>.</li>

    <li><code>git commit <em>files</em></code> 进行一次包含最后一次提交加上工作目录中文件快照的提交。并且文件被添加到暂存区域。</li>

    <li><code>git checkout HEAD -- <em>files</em></code> 回滚到复制最后一次提交。</li>

  </ul>

  <h2 id="conventions">约定</h2>

  <p>后文中以下面的形式使用图片。</p>

  <div class="center"><img src='conventions.svg.png'></div>

  <p>绿色的5位字符表示提交的ID，分别指向父节点。分支用橘色显示，分别指向特定的提交。当前分支由附在其上的<em>HEAD</em>标识。
这张图片里显示最后5次提交，<em>ed489</em>是最新提交。 <em>master</em>分支指向此次提交，另一个<em>maint</em>分支指向祖父提交节点。</p>

  <h2 id="commands-in-detail">命令详解</h2>

  <h3 id="diff">Diff</h3>

  <p>有许多种方法查看两次提交之间的变动。下面是一些示例。</p>

  <div class="center"><img src='diff.svg.png'></div>

  <h3 id="commit">Commit</h3>

  <p>提交时，git用暂存区域的文件创建一个新的提交，并把此时的节点设为父节点。然后把当前分支指向新的提交节点。下图中，当前分支是<em>master</em>。
在运行命令之前，<em>master</em>指向<em>ed489</em>，提交后，<em>master</em>指向新的节点<em>f0cec</em>并以<em>ed489</em>作为父节点。</p>

  <div class="center"><img src='commit-master.svg.png'></div>

  <p>即便当前分支是某次提交的祖父节点，git会同样操作。下图中，在<em>master</em>分支的祖父节点<em>maint</em>分支进行一次提交，生成了<em>1800b</em>。
这样，<em>maint</em>分支就不再是<em>master</em>分支的祖父节点。此时，<a href='#merge'>合并</a> (或者 <a href='#rebase'>衍合</a>) 是必须的。</p>

  <div class="center"><img src='commit-maint.svg.png'></div>

  <p>如果想更改一次提交，使用  <code>git commit --amend</code>。git会使用与当前提交相同的父节点进行一次新提交，旧的提交会被取消。</p>

  <div class="center"><img src='commit-amend.svg.png'></div>

  <p>另一个例子是<a href="#detached">分离HEAD提交</a>,后文讲。</p>

  <h3 id="checkout">Checkout</h3>

  <p>checkout命令用于从历史提交（或者暂存区域）中拷贝文件到工作目录，也可用于切换分支。</p>

  <p>当给定某个文件名（或者打开-p选项，或者文件名和-p选项同时打开）时，git会从指定的提交中拷贝文件到暂存区域和工作目录。比如，<code>git checkout HEAD~ foo.c</code>会将提交节点<em>HEAD~</em>(即当前提交节点的父节点)中的<code>foo.c</code>复制到工作目录并且加到暂存区域中。（如果命令中没有指定提交节点，则会从暂存区域中拷贝内容。）注意当前分支不会发生变化。</p>

  <div class="center"><img src='checkout-files.svg.png'></div>

  <p>当不指定文件名，而是给出一个（本地）分支时，那么<em>HEAD</em>标识会移动到那个分支（也就是说，我们“切换”到那个分支了），然后暂存区域和工作目录中的内容会和<em>HEAD</em>对应的提交节点一致。新提交节点（下图中的a47c3）中的所有文件都会被复制（到暂存区域和工作目录中）；只存在于老的提交节点（ed489）中的文件会被删除；不属于上述两者的文件会被忽略，不受影响。</p>

  <div class="center"><img src='checkout-branch.svg.png'></div>

  <p>如果既没有指定文件名，也没有指定分支名，而是一个标签、远程分支、SHA-1值或者是像<em>master~3</em>类似的东西，就得到一个匿名分支，称作<em>detached HEAD</em>（被分离的<em>HEAD</em>标识）。这样可以很方便地在历史版本之间互相切换。比如说你想要编译1.6.6.1版本的git，你可以运行<code>git checkout v1.6.6.1</code>（这是一个标签，而非分支名），编译，安装，然后切换回另一个分支，比如说<code>git checkout master</code>。然而，当提交操作涉及到“分离的HEAD”时，其行为会略有不同，详情见在<a href="#detached">下面</a>。</p>

  <div class="center"><img src='checkout-detached.svg.png'></div>

  <h3 id="detached">HEAD标识处于分离状态时的提交操作</h3>

  <p>当<em>HEAD</em>处于分离状态（不依附于任一分支）时，提交操作可以正常进行，但是不会更新任何已命名的分支。(你可以认为这是在更新一个匿名分支。)</p>

  <div class="center"><img src='commit-detached.svg.png'></div>

  <p>一旦此后你切换到别的分支，比如说<em>master</em>，那么这个提交节点（可能）再也不会被引用到，然后就会被丢弃掉了。注意这个命令之后就不会有东西引用<em>2eecb</em>。</p>

  <div class="center"><img src='checkout-after-detached.svg.png'></div>

  <p>但是，如果你想保存这个状态，可以用命令<code>git checkout -b <em>name</em></code>来创建一个新的分支。</p>

  <div class="center"><img src='checkout-b-detached.svg.png'></div>

  <h3 id="reset">Reset</h3>

  <p>reset命令把当前分支指向另一个位置，并且有选择的变动工作目录和索引。也用来在从历史仓库中复制文件到索引，而不动工作目录。</p>

  <p>如果不给选项，那么当前分支指向到那个提交。如果用<code>--hard</code>选项，那么工作目录也更新，如果用<code>--soft</code>选项，那么都不变。</p>

  <div class="center"><img src='reset-commit.svg.png'></div>

  <p>如果没有给出提交点的版本号，那么默认用<em>HEAD</em>。这样，分支指向不变，但是索引会回滚到最后一次提交，如果用<code>--hard</code>选项，工作目录也同样。</p>

  <div class="center"><img src='reset.svg.png'></div>

  <p>如果给了文件名(或者 <code>-p</code>选项), 那么工作效果和带文件名的<a href='#checkout'>checkout</a>差不多，除了索引被更新。</p>

  <div class="center"><img src='reset-files.svg.png'></div>

  <h3 id="merge">Merge</h3>

  <p>merge 命令把不同分支合并起来。合并前，索引必须和当前提交相同。如果另一个分支是当前提交的祖父节点，那么合并命令将什么也不做。
  另一种情况是如果当前提交是另一个分支的祖父节点，就导致<em>fast-forward</em>合并。指向只是简单的移动，并生成一个新的提交。</p>

  <div class="center"><img src='merge-ff.svg.png'></div>

  <p>否则就是一次真正的合并。默认把当前提交(<em>ed489</em> 如下所示)和另一个提交(<em>33104</em>)以及他们的共同祖父节点(<em>b325c</em>)进行一次<a
    href='http://en.wikipedia.org/wiki/Three-way_merge'>三方合并</a>。结果是先保存当前目录和索引，然后和父节点<em>33104</em>一起做一次新提交。
  </p>

  <div class="center"><img src='merge.svg.png'></div>

  <h3 id="cherry-pick">Cherry Pick</h3>

  <p>cherry-pick命令"复制"一个提交节点并在当前分支做一次完全一样的新提交。</p>

  <div class="center"><img src='cherry-pick.svg.png'></div>

  <h3 id="rebase">Rebase</h3>

  <p>衍合是合并命令的另一种选择。合并把两个父分支合并进行一次提交，提交历史不是线性的。衍合在当前分支上重演另一个分支的历史，提交历史是线性的。
  本质上，这是线性化的自动的 <a href='#cherry-pick'>cherry-pick</a></p>

  <div class="center"><img src='rebase.svg.png'></div>

  <p>上面的命令都在<em>topic</em>分支中进行，而不是<em>master</em>分支，在<em>master</em>分支上重演，并且把分支指向新的节点。注意旧提交没有被引用，将被回收。</p>

  <p>要限制回滚范围，使用<code>--onto</code>选项。下面的命令在<em>master</em>分支上重演当前分支从<em>169a6</em>以来的最近几个提交，即<em>2c33a</em>。</p>

  <div class="center"><img src='rebase-onto.svg.png'></div>

  <p>同样有<code>git rebase --interactive</code>让你更方便的完成一些复杂操作，比如丢弃、重排、修改、合并提交。没有图片体现这些，细节看这里:<a
    href='http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html#_interactive_mode'>git-rebase(1)</a></p>

  <h2 id="technical-notes">技术说明</h2>

  <p>文件内容并没有真正存储在索引(<em>.git/index</em>)或者提交对象中，而是以blob的形式分别存储在数据库中(<em>.git/objects</em>)，并用SHA-1值来校验。
索引文件用识别码列出相关的blob文件以及别的数据。对于提交来说，以树(<em>tree</em>)的形式存储，同样用对于的哈希值识别。树对应着工作目录中的文件夹，树中包含的
树或者blob对象对应着相应的子目录和文件。每次提交都存储下它的上一级树的识别码。</p>

  <p>如果用detached HEAD提交，那么最后一次提交会被the reflog for HEAD引用。但是过一段时间就失效，最终被回收，与<code>git commit --amend</code>或者<code>git
    rebase</code>很像。</p>

  <hr>

  <p>Copyright &copy; 2010,
    <a href='mailto:lodatom@gmail.com'>Mark Lodato</a>.
  Chinese translation &copy; 2012,
    <a href='mailto:ellrywych@gmail.com'>wych</a>.
  </p>

  <p><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/3.0/us/"><img alt="" src="https://i.creativecommons.org/l/by-nc-sa/3.0/us/80x15.png"/></a> 本著作系采用<a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/3.0/us/">创用CC 姓名标示-非商业性-相同方式分享3.0 美国授权条款</a>授权。</p>

</body>
</html>
